package jmine.tec.di.resolver.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jmine.tec.annotations.Unmodifiable;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.di.type.TypeConverter;
import jmine.tec.di.type.TypeConvertionException;
import jmine.tec.di.wrapper.Wrapper;

/**
 * @author MarceloT
 */
@Unmodifiable
public class RegexMatchingResolver extends AbstractManyObjectsResolver {
    private final Pattern pattern;

    private final int groupIndex;

    /**
     * @param desiredType class
     * @param regex regex
     * @param typeConverter o conversor
     * @param groupIndex o indice
     * @param keyType class
     * @param keyTypeConverter type converter
     */
    public RegexMatchingResolver(Class<?> desiredType, String regex, TypeConverter<?> typeConverter, int groupIndex, Class<?> keyType,
            TypeConverter<?> keyTypeConverter) {
        super(desiredType, typeConverter, keyType, keyTypeConverter);
        this.pattern = Pattern.compile(regex);
        this.groupIndex = groupIndex;
    }

    /**
     * @param name {@link String}
     * @return boolean
     */
    @Override
    protected boolean matches(String name) {
        return this.pattern.matcher(name).matches();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object resolveObject(TypedScope properties, Wrapper wrapper, Class<?> requiredComponentClass) throws TypeConvertionException {
        Map<String, String> all = this.collectRecurse(properties, new HashMap<String, String>());
        final int size = all.size();
        Object[] selected = new Object[size];
        Object[] values = new Object[size];
        int i = 0;
        Set<Entry<String, String>> entrySet = all.entrySet();
        for (Entry<String, String> entry : entrySet) {
            selected[i] = this.convertKey(entry.getValue());
            values[i++] = this.getObjectConvertType(properties, entry.getKey());
        }
        return wrapper.wrap(selected, values, requiredComponentClass);
    }

    /**
     * Devolve um mapa com o nome da propriedade -> grupo selecionado
     * 
     * @param props {@link TypedScope}
     * @param acc {@link Set}
     * @return {@link Set}
     */
    private Map<String, String> collectRecurse(TypedScope props, Map<String, String> acc) {
        if (props == null) {
            return acc;
        }
        for (Entry<String, Object> entry : props.getProperties().entrySet()) {
            Matcher matcher = this.pattern.matcher(entry.getKey());
            if (matcher.matches()) {
                acc.put(entry.getKey(), matcher.group(this.groupIndex));
            }
        }
        return this.collectRecurse(props.getParent(), acc);
    }

    /**
     * Devolve a pattern utilizada na expressao regular
     * 
     * @return {@link String}
     */
    public String getPattern() {
        return this.pattern.pattern();
    }

}
